﻿using System;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Threading.Tasks;

namespace NazTek.Core
{
    /// <summary>
    /// Captures <see cref="DbContext"/> API for test and repository.
    /// </summary>
    public interface IDbContext : IDisposable
    {
        /// <summary>
        /// Gets/sets logging delegate.
        /// </summary>
        Action<string> Log { get; set; }
        /// <summary>
        /// Gets a System.Data.Entity.Infrastructure.DbEntityEntry&lt;T&gt; object for the given entity providing 
        /// access to information about the entity and the ability to perform actions on the entity.
        /// </summary>
        /// <typeparam name="T">The type of the entity.</typeparam>
        /// <param name="entity">The entity.</param>
        /// <returns>An entry for the entity.</returns>
        DbEntityEntry<T> Entry<T>(T entity) where T : class;
        /// <summary>
        /// Returns a System.Data.Entity.DbSet&lt;T/&gt; instance for access to entities of the given type in the context and the underlying store.
        /// </summary>
        /// <typeparam name="T">The type entity for which a set should be returned.</typeparam>
        /// <returns>A set for the given entity type.</returns>
        DbSet<T> Set<T>() where T : class;
        /// <summary>
        /// Saves all changes made in this context to the underlying database.
        /// </summary>
        /// <returns>
        /// The number of state entries written to the underlying database. This can include state entries for entities 
        /// and/or relationships. Relationship state entries are created for many-to-many relationships and relationships where 
        /// there is no foreign key property included in the entity class (often referred to as independent associations).
        /// </returns>
        int SaveChanges();
        /// <summary>
        /// Asynchronously saves all changes made in this context to the underlying database.
        /// </summary>
        /// <returns>
        /// A task that represents the asynchronous save operation.  The task result contains the number of state entries written to the underlying 
        /// database. This can include state entries for entities and/or relationships. Relationship state entries are created for many-to-many relationships 
        /// and relationships where there is no foreign key property included in the entity class (often referred to as independent associations).
        /// </returns>
        /// <remarks>
        /// Multiple active operations on the same context instance are not supported. Use 'await' to ensure that any 
        /// asynchronous operations have completed before calling another method on this context.
        /// </remarks>
        Task<int> SaveChangesAsync();
        /// <summary>
        /// Executes dynamic SQL.
        /// </summary>
        /// <typeparam name="T">The entity type of the result.</typeparam>
        /// <param name="query">The query string to execute.</param>
        /// <returns>Typed query result.</returns>
        DbRawSqlQuery<T> ExecuteQuery<T>(string query);
        /// <summary>
        /// Gets the active DbContext instance.
        /// </summary>
        DbContext CurrentDbContext { get; }
    }
    /// <summary>
    /// Provides basic database context implementation.
    /// </summary>
    public abstract class DefaultDbContext : DbContext, IDbContext
    {
        /// <summary>
        /// Gets/sets <see cref="Database.Log">underlying logging delegate</see>.
        /// </summary>
        public Action<string> Log
        {
            get { return Database.Log; }
            set { Database.Log = value; }
        }
        /// <summary>
        /// Executes dynamic SQL.
        /// </summary>
        /// <typeparam name="T">The entity type of the result.</typeparam>
        /// <param name="query">The query string to execute.</param>
        /// <returns>Typed query result.</returns>
        public DbRawSqlQuery<T> ExecuteQuery<T>(string query)
        {
            return Database.SqlQuery<T>(query);
        }
        /// <summary>
        /// Gets the active DbContext instance.
        /// </summary>
        public DbContext CurrentDbContext { get { return this; } }
        /// <summary>
        /// Creates instance with default name.
        /// </summary>
        protected DefaultDbContext() : this("name=DefaultDbContext") { }
        /// <summary>
        /// Constructs a new context instance using the given string as the name or connection string for the database 
        /// to which a connection will be made.  See the class remarks for how this is used to create a connection.
        /// </summary>
        /// <param name="nameOrConnectionString">Either the database name or a connection string.</param>
        protected DefaultDbContext(string nameOrConnectionString) : base(nameOrConnectionString) { }
    }
}